fix(dav): send reply email to organizer when attendee responds via invitation link#61213
fix(dav): send reply email to organizer when attendee responds via invitation link#61213ndo84bw wants to merge 1 commit into
Conversation
…vitation link Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Nico Donath <ndo84bw@gmx.de>
|
Thank you for your contribution! If you are using AI, please follow our AI Policy. E.g.:
Seeing only a wall of text generated by an LLM often means that the author did not really look at the generated code and shifts all work to the reviewers. This is not a good and fair approach, and cannot scale properly on open source projects. |
Summary
When an attendee responds to a calendar invitation via the Accept/Decline links in the invitation email (
/apps/dav/invitation/{accept,decline}/{token}), the attendee's participation status is updated on the organizer's event, but the organizer is never notified by email. Responding to the same invitation through the Calendar web UI or through a CalDAV client does notify the organizer. nextcloud/calendar#7636 reports this inconsistency.Root cause
The invitation links are handled by
InvitationResponseController, which delivers the reply through a dedicated minimal DAV server,InvitationResponseServer. Delivering the reply to the attendee's calendar already triggers sabre's scheduling cascade (Sabre\CalDAV\Schedule\Plugin::scheduleLocalDelivery()→processICalendarChange()→deliver()): a second iTip REPLY addressed to the organizer is generated and emitted as ascheduleevent. This is how the organizer's copy of the event gets updated today.Sending the email is the responsibility of
IMipPlugin, which listens on that samescheduleevent. On the regular DAV server it is registered (apps/dav/lib/Server.php), onInvitationResponseServerit is not. The event that would produce the organizer email fires with nobody listening.Fix
Register
IMipPluginon theInvitationResponseServer, but only inhandleITipMessage(), which is exclusively used by the invitation-link controller. It must not be registered unconditionally in the constructor: the same server class is also used byCalendarImpl(e.g. iMIP processing for the Mail app) and byICreateFromString::createFromStringMinimal(), whose API documentation explicitly guarantees "no iMIP plugin, no invitation emails". Registering the plugin globally would cause duplicate emails in those flows.With this change, responding via the email links produces the same organizer notification as responding via the Calendar web UI, generated by the same broker/IMip machinery.
Known limitation (intentionally out of scope)
Attendees that are not users on the instance (external attendees) still do not trigger an organizer email when they use the links. Their reply takes a different path: it is delivered directly into the organizer's calendar via
scheduleLocalDelivery()without ever passing through thescheduleevent, and the token-built REPLY object lacks the event data (DTSTART,SUMMARY) needed to render the email. Supporting that case requires enriching the reply from the stored event first, which is a considerably larger change. I would prefer to address it in a follow-up PR so this fix is not blocked by that discussion. Whether nextcloud/calendar#7636 can be closed with this PR alone or only together with the follow-up is up to the maintainers.TODO
Manual tests
Performed on a Nextcloud 33.0.3 instance with this change applied (
handleITipMessage()is identical on master and stable33). User A (organizer) and user B (attendee) are users on the instance, user C is an external attendee invited by email address only.Checklist
InvitationResponseServerTestasserts that the constructor does not register the iMIP plugin (thecreateFromStringMinimal()guarantee) and thathandleITipMessage()does.3. to review, feature component)stable32)AI (if applicable)